const request = require('request');
const md5 = require('MD5');
const uuid = require("uuid");
const xml2js = require('xml2js');

const parseXML = new xml2js.Parser({ trim: true, explicitArray: false, explicitRoot: false }).parseStringPromise;
const bud = new xml2js.Builder();
function buildXML(p) {
  return bud.buildObject(p);
}
const NS = ["number", "string"];
const excludeSign = ["pfx", "partner_key", "sign", "key"];

class WXPAY {
  options = {};
  constructor(p) {
    Object.assign(this.options, p);
    this.wxPayId = { appid: this.options.appid, mch_id: this.options.mch_id };
  }
  get check() { return this.options.appid && this.options.mch_id && this.options.partner_key && this.options.pfx; }
  /**
   * 获取随机字符串
   * @param {uint} length 整数型,默认长度32
   */
  generateNonceString(length) { // 获取随机字符串
    let t = uuid.v1().replace(/-/ig, "");
    if (!(length > 0)) length = 32;
    while (length > t) { t += uuid.v1().replace(/-/ig, ""); }
    return t.substr(0, length);
  }
  isNSt(p) {
    return NS.indexOf(typeof p) >= 0 && !!p;
  }
  // ----------------------------util1 结束---wxPay开始----------------------------------------------------
  sign(param) {
    if (!this.isNSt(param.nonce_str)) param.nonce_str = this.generateNonceString();
    let t = Object.keys(param).filter((key) => {
      return this.isNSt(param[key]) && excludeSign.indexOf(key) < 0;
    }).sort().map((key) => {
      return key + '=' + param[key];
    }).join("&") + "&key=" + this.options.partner_key;
    param.sign = md5(t).toUpperCase();
    return param;
    // return md5(t).toUpperCase();
  }

  /** 统一下单接口 扫码支付方式二 非固定二维码
   * @param {Object} opts 包含成员如下:
   * @body string 商品描述,支付页面的标题
   * @out_trade_no string 订单编号.最大长度32
   * @total_fee int 支付金额(单位:分)
   * @spbill_create_ip string 付款者ip
   * @notify_url string 本次支付后,回调url
   * @trade_type string 支付类型 
   * JSAPI--JSAPI支付（或小程序支付）
   * NATIVE--Native支付
   * APP--app支付
   * MWEB--H5支付
  */
  createOrder(opts) { // 创建订单
    Object.assign(opts, this.wxPayId);
    return new Promise((resolve) => {
      request({
        url: "https://api.mch.weixin.qq.com/pay/unifiedorder",
        method: 'POST',
        body: buildXML(this.sign(opts)),
        agentOptions: { pfx: this.options.pfx, passphrase: this.options.mch_id }
      }, (err, response, body) => {
        if (err) resolve(err); else parseXML(body).then(resolve).catch(resolve);
      }
      );
    }
    );
  }

  /**订单查询
   * @out_trade_no string 本地订单号
   */
  queryOrder(out_trade_no) {
    return new Promise((resolve) => {
      if (typeof out_trade_no == "string" /* && out_trade_no.length == 32 */) {
        let t = { xml: this.sign(Object.assign({ out_trade_no: out_trade_no }, this.wxPayId)) };
        t = buildXML(t);
        request({ url: "https://api.mch.weixin.qq.com/pay/orderquery", method: "POST", body: t },
          (err, response, body) => {
            if (err) resolve(err); else parseXML(body).then(resolve).catch(resolve);
          });
      } else resolve();
    }
    );
  }

  /**订单退款 成员如下:
   * @out_trade_no 商户订单编号
   * @total_fee 订单总金额(单位:分)，只能为整数
   * @refund_fee 退款总金额(单位:分)，只能为整数，可部分退款。
   * @notify_url 退款结果通知url.
   * @out_refund_no 商户退款单号.同一退款单号多次请求只退一笔.
   */
  refundOrder(order) {
    Object.assign(order, this.wxPayId);
    console.log(order)
    return new Promise((resolve, reject) => {
      request({
        url: "https://api.mch.weixin.qq.com/secapi/pay/refund",
        method: "POST",
        body: buildXML({ xml: this.sign(order) }),
        agentOptions: {
          pfx: this.options.pfx,
          passphrase: this.options.mch_id
        }
      }, (err, response, body) => {
        console.log(err,response,body)
        if (err) resolve(err); else parseXML(body).then(resolve).catch(resolve);
      });
    });
  }

  /**订单关闭
   * @out_trade_no 商户订单号
   */
  closeOrder(out_trade_no) {
    return new Promise(resolve => {
      request({
        url: "https://api.mch.weixin.qq.com/pay/closeorder",
        method: "POST",
        body: buildXML({ xml: this.sign(Object.assign({ out_trade_no: out_trade_no }, this.wxPayId)) })
      }, (err, res, body) => {
        if (err) resolve(err); else parseXML(body).then(resolve).catch(resolve);
      });
    });
  }

};
exports = module.exports = WXPAY;